package com.qm.aop;

import com.qm.anno.CacheFind;
import com.qm.anno.CacheUpdate;
import com.qm.util.ObjectMapperUtil;
import org.apache.ibatis.reflection.ArrayUtil;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.ShardedJedis;

/**
 @author james
 @create 2021-04-09 11:19
 AOP入门案例  and  AOP实现redis缓存业务
 */

@Component//将类交给spring容器进行管理
@Aspect//定义aop切面
public class CacheAOP {

    @Autowired(required = false)
//    private Jedis jedis;              //单台redis注入
//    private ShardedJedis jedis;         //分片redis注入  性能更高  内存更大
    private JedisCluster jedis;

    /**
     * 实现思路： 拦截被@CacheFind标识的方法 之后利用aop进行缓存的控制
     * 通知方法： 环绕通知 Around
     * 实现步骤：
     *      1.准备查询redis的key     ITEM_CAT_LIST::第一个参数。
     *      2.@annotation(cacheFind)动态获取注解的语法。
     *        拦截指定参数类型的注解并且将注解对象当做参数进行传递。
     */
//    @Around("@annotation(com.qm.anno.CacheFind)")
    @Around("@annotation(cacheFind)")
    public Object around(ProceedingJoinPoint joinPoint, CacheFind cacheFind) {

        String key = cacheFind.key();
        Object[] args = joinPoint.getArgs();
        if (args.length > 0) {
            String firstArg = joinPoint.getArgs()[0].toString();
            key += "::" + firstArg;   //key+参数值生成redis的key
        }
        Object result = null;
        if (jedis.exists(key)) {
            String jsonObject = jedis.get(key);
            MethodSignature methodSignature = (MethodSignature)joinPoint.getSignature();
            Class returnType = methodSignature.getReturnType();
            result = ObjectMapperUtil.toObject(jsonObject, returnType);
//            System.out.println("AOP缓存查询");
        } else {
            try {
                result = joinPoint.proceed();
                String json = ObjectMapperUtil.toJSON(result);
                if (cacheFind.secondes()>0) {
                    jedis.setex(key, cacheFind.secondes(), json);
                } else {
                    jedis.set(key, json);
                }
//                System.out.println("AOP查询数据库");
            } catch (Throwable e) {
                e.printStackTrace();
                throw new RuntimeException();
            }
        }
        return result;
    }

    @Around("@annotation(cacheUpdate)")
    public Object itemUpdateAround(ProceedingJoinPoint joinPoint, CacheUpdate cacheUpdate) {
        String key = cacheUpdate.key();
        Object result = null;
        if (jedis.exists(key)) {
            jedis.del(key);
        }
        try {
            result = joinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            throw new RuntimeException();
        }
       return result;
    }

    //公式： 切面=切入点表达式 + 通知方法
    /**
     * 义务需求：要求拦截ItemCatServicefImpl类中的业务
     */
//    @Pointcut("bean(itemCatServiceImpl)") //按类匹配，控制的粒度较粗  单个bean
//    @Pointcut("within(com.qm.service..*)")  //按类匹配，控制的粒度较粗  多个bean(.当前包的类  ..子包的类)
//    @Pointcut("execution(* com.qm.service..*.*(..))")
//    public void pointCut() {
//
//    }
//
//    //joinPoint 方法执行
//    @Before(value = "pointCut()")
//    public void before(JoinPoint joinPoint) {
//        System.out.println("我是前置通知");
//        String typeName = joinPoint.getSignature().getDeclaringTypeName();
//        String methodName = joinPoint.getSignature().getName();
//        System.out.println("方法执行的全路径名：" + typeName + methodName);//com.qm.service.impl.ItemCatServiceImplfindItemCatById
//
//    }
//
//    //添加环绕通知 环绕通知可以控制目标方法执行 要求添加参数
//    @Around("pointCut()")
//    public Object Around(ProceedingJoinPoint joinPoint) {
//        System.out.println("我是环绕通知开始");
//        try {
//            Object result = joinPoint.proceed();
//            System.out.println("我是环绕通知结束");
//            return result;
//        } catch (Throwable throwable) {
//            throwable.printStackTrace();
//            throw new RuntimeException();
//        }
//    }


}
